﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace SKS
{
    public class TouchEngine : MonoBehaviour
    {
        private static TouchEngine _instance;
        internal event EventHandler<ApplicationTokenEventArgs> TokenPlacedOnScreen;
        internal event EventHandler<ApplicationTokenEventArgs> TokenRemovedFromScreen;
        internal event EventHandler<ApplicationTokenEventArgs> ScreenTokenUpdated;
        
        
        private void Awake()
        {
            if (_instance == null) {
                _instance = this;
            }
        }

        void Start()
        {
            //InitEngine();
        }

        readonly object TokenCallBackLock = new object();
        
        public void LaunchTokenPlacedOnScreen(ApplicationTokenEventArgs e)
        {
            EventHandler<ApplicationTokenEventArgs> handler;
            List<Delegate> subscribers = new List<Delegate>();
            int? invokerTokenClass = null;

            lock (TokenCallBackLock)
            {
                handler = TokenPlacedOnScreen;
            }

            if (handler != null)
            {
                subscribers = handler.GetInvocationList().ToList();
                invokerTokenClass = e.Token.Class;
                DispatchEventToSubscribers(subscribers, invokerTokenClass, e);
            }
        }

        public void LaunchTokenRemovedFromScreen(ApplicationTokenEventArgs e)
        {
            EventHandler<ApplicationTokenEventArgs> handler;
            List<Delegate> subscribers = new List<Delegate>();
            int? invokerTokenClass = null;

            lock (TokenCallBackLock)
            {
                handler = TokenRemovedFromScreen;
            }

            if (handler != null)
            {
                subscribers = handler.GetInvocationList().ToList();
                invokerTokenClass = e.Token.Class;
                DispatchEventToSubscribers(subscribers, invokerTokenClass, e);
            }
        }
        
        public void LaunchScreenTokenUpdated(ApplicationTokenEventArgs e)
        {
            EventHandler<ApplicationTokenEventArgs> handler;
            List<Delegate> subscribers = new List<Delegate>();
            int? invokerTokenClass = null;

            lock (TokenCallBackLock)
            {
                handler = ScreenTokenUpdated;
            }

            if (handler != null)
            {
                subscribers = handler.GetInvocationList().ToList();
                invokerTokenClass = e.Token.Class;
                DispatchEventToSubscribers(subscribers, invokerTokenClass, e);
            }
        }
        
        
        private void DispatchEventToSubscribers(List<Delegate> subscribers, int? invokerTokenClass, ApplicationTokenEventArgs e)
        {
            foreach (Delegate subscriber in subscribers)
            {
                IApplicationToken applicationToken = subscriber.Target as IApplicationToken;
                int subscriberTokenClass = applicationToken.TokenClass;

                if (invokerTokenClass == subscriberTokenClass)
                {
                    EventHandler<ApplicationTokenEventArgs> subscriberHandler = subscriber as EventHandler<ApplicationTokenEventArgs>;
                    subscriberHandler(this, e);
                }
            }
        }
        
        public static TouchEngine Instance => _instance ?? (_instance = GetOrCreateInstance());

        private static TouchEngine GetOrCreateInstance()
        {
            if (_instance != null) return _instance;
            var te = FindObjectOfType<TouchEngine>();
            if (te == null) {
                var tes = Resources.FindObjectsOfTypeAll<TouchEngine>();
                if (tes.Length > 0) {
                    te = Instantiate(tes[0]);
                }
                if (tes.Length > 1) {
                    Debug.LogWarning("TouchEngine有多个实例.");
                }
            }
            if (te == null) {
                var go = new GameObject("SKS.TouchEngine");
                te = go.AddComponent<TouchEngine>();
            }
            if (!te.gameObject.activeInHierarchy) {
                te.gameObject.SetActive(true);
            }
            return te;
        }
    }
    
    public class ApplicationTokenEventArgs : EventArgs
    {
        private Token _token;
        public Token Token { get { return _token; } }

        internal ApplicationTokenEventArgs(Token token)
        {
            this._token = token;
        }
    }
}

